package com.ihr360.payroll.service.salarytasklet.step.processor;

import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.meta.table.DynamicCell;
import com.ihr360.commons.model.type.FieldSource;
import com.ihr360.commons.model.type.FormulaFrom;
import com.ihr360.expr.engine.DependencyEngine;
import com.ihr360.job.core.annotation.AfterStep;
import com.ihr360.job.core.annotation.BeforeProcess;
import com.ihr360.job.core.item.ItemProcessor;
import com.ihr360.job.core.repository.JobRepository;
import com.ihr360.job.core.scope.context.SimpleStepContext;
import com.ihr360.payperiod.PayPeriodTerm;
import com.ihr360.payroll.dao.MergeReportStaffSalaryRepository;
import com.ihr360.payroll.dao.MergeSalaryReportTermSalaryRepository;
import com.ihr360.payroll.dao.StaffPayPeriodTermSalaryRepository;
import com.ihr360.payroll.entity.staff.CompanySalaryProfileEntity;
import com.ihr360.payroll.entity.staff.SalaryProfileFieldEntity;
import com.ihr360.payroll.model.base.ISalaryField;
import com.ihr360.payroll.model.dto.salary.basecode.SalaryFieldType;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.report.MergeReportStaffSalary;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReportTermSalary;
import com.ihr360.payroll.model.dto.salary.superclass.AbstractSalaryFieldValue;
import com.ihr360.payroll.model.dto.salary.task.period.StaffPayPeriodTermSalary;
import com.ihr360.payroll.model.dto.salary.task.period.embeddable.StaffSalarySnapshot;
import com.ihr360.payroll.model.dto.salary.task.period.embeddable.StaffSalaryTermId;
import com.ihr360.payroll.model.response.salaryreport.SalaryHeader;
import com.ihr360.payroll.service.salaryprofile.CompanySalaryProfileService;
import com.ihr360.payroll.service.salarytasklet.step.SalaryCalcContext;
import com.ihr360.payroll.type.ReportState;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.validation.ValidationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * @author davidwei
 */
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MergeTaxSalaryReportDataProcessor implements ItemProcessor<List<String>, List<MergeReportStaffSalary>> {
    final Logger logger = LoggerFactory.getLogger(DependencyEngine.class.getName());
    private SimpleStepContext simpleStepContext;

    private SalaryCalcContext salaryCalcContext;
    @Autowired
    private JobRepository jobRepository;

    @Autowired
    private StaffPayPeriodTermSalaryRepository staffPayPeriodTermSalaryRepository;

    @Autowired
    private MergeReportStaffSalaryRepository mergeReportStaffSalaryRepository;
    @Autowired
    private CompanySalaryProfileService companySalaryProfileService;

    @Autowired
    MergeSalaryReportTermSalaryRepository mergeSalaryReportTermSalaryRepository;

    public void setSalaryCalcContext(SalaryCalcContext salaryCalcContext) {
        ValidationUtils.rejectIfEmpty(salaryCalcContext, "[薪资报表]", "计算上下文变量为空");
        this.salaryCalcContext = salaryCalcContext;
    }

    @Override
    public List<MergeReportStaffSalary> process(List<String> staffIds) throws Exception {
        PayPeriodTerm payPeriodTerm = salaryCalcContext.payPeriodTerm;
        ValidationUtils.rejectIfEmpty(payPeriodTerm, "[薪资报表]", "无法获取账期薪资");

        Long salaryReportTermSalaryId = payPeriodTerm.getMergeSalaryReportTermSalaryId();

        String companyId = salaryCalcContext.getCompanyId();
        Long basedOnTermId = payPeriodTerm.getBasedOnTermId();
        Long termId = payPeriodTerm.getId();

        List<StaffPayPeriodTermSalary> basedOnData = staffPayPeriodTermSalaryRepository.findByCompanyIdAndIdTermIdAndIdStaffIdIn(companyId, basedOnTermId, staffIds);
        Map<String, StaffPayPeriodTermSalary> baseOnDataMap = basedOnData.stream().collect(Collectors.toMap(s -> s.getId().getStaffId(), s -> s));

        List<StaffPayPeriodTermSalary> data = staffPayPeriodTermSalaryRepository.findByCompanyIdAndIdTermIdAndIdStaffIdIn(companyId, termId, staffIds);
        Map<String, StaffPayPeriodTermSalary> dataMap = data.stream().collect(Collectors.toMap(s -> s.getId().getStaffId(), s -> s));
        Map<String, SalaryField> salaryFieldMap = salaryCalcContext.salaryFieldsHolder.getWrittenItem();

        MergeSalaryReportTermSalary mergeSalaryReportTermSalary = mergeSalaryReportTermSalaryRepository.findOne(salaryReportTermSalaryId);
        ValidationUtils.rejectIfEmpty(payPeriodTerm, "[薪资报表]", "无法找到合并计税汇总表定义");

        SalaryHeader salaryHeader = mergeSalaryReportTermSalary.getSalaryHeader();
        ValidationUtils.rejectIfEmpty(salaryHeader, "[薪资报表]", "无法合并计税汇总表头定义");
        ValidationUtils.rejectIfEmpty(salaryHeader.getHeadMap(), "[薪资报表]", "无法合并计税汇总表头定义");

        Set<String> fieldSet = salaryHeader.getHeadMap().values().stream().map(s -> s.getColumnName()).collect(Collectors.toSet());


        CompanySalaryProfileEntity companySalaryProfile = companySalaryProfileService.getByCompanyId(companyId);
        Set<String> companyProfileSet = new HashSet<>(10);
        if (companySalaryProfile != null) {
            List<SalaryProfileFieldEntity> profileFields = companySalaryProfile.getProfileFields();
            if (CollectionUtils.isNotEmpty(profileFields)) {
                companyProfileSet = profileFields.stream().map(f -> f.getCode()).collect(Collectors.toSet());
            }
        }

        List<MergeReportStaffSalary> reports = new ArrayList<>();
        for (String staffId : staffIds) {
            StaffPayPeriodTermSalary basedOnTermSalary = baseOnDataMap.get(staffId);
            StaffPayPeriodTermSalary termSalary = dataMap.get(staffId);
            StaffSalarySnapshot basedOnSnapshot = null;
            StaffSalarySnapshot snapshot = null;
            if (basedOnTermSalary != null) {
                basedOnSnapshot = basedOnTermSalary.getSummarizedData();
            }
            if (termSalary != null) {
                snapshot = termSalary.getSummarizedData();
            }
            Map<String, DynamicCell> basedOnSnapshotData = null;
            if (basedOnSnapshot != null) {
                basedOnSnapshotData = basedOnSnapshot.getData();
            }
            Map<String, DynamicCell> snapshotData = null;
            if (snapshot != null) {
                snapshotData = snapshot.getData();
            }
            MergeReportStaffSalary mergeReportStaffSalary = new MergeReportStaffSalary();
            mergeReportStaffSalary.setId(new StaffSalaryTermId(salaryReportTermSalaryId, staffId));
            mergeReportStaffSalary.setCompanyId(companyId);
            mergeReportStaffSalary.setTermSalary(new MergeSalaryReportTermSalary(salaryReportTermSalaryId));


            if (!CollectionUtils.isEmpty(basedOnSnapshotData) && CollectionUtils.isEmpty(snapshotData)) {
                mergeReportStaffSalary.setSummarizedData(basedOnSnapshot);
            } else if (CollectionUtils.isEmpty(basedOnSnapshotData) && !CollectionUtils.isEmpty(snapshotData)) {
                mergeReportStaffSalary.setSummarizedData(snapshot);
            } else {
                StaffSalarySnapshot result = new StaffSalarySnapshot();
                for (String field : fieldSet) {
                    DynamicCell basedPart = basedOnSnapshotData.get(field);
                    DynamicCell part = snapshotData.get(field);
                    ISalaryField salaryField = getSalaryField(salaryFieldMap, field);
                    if (salaryField != null) {
                        SalaryFieldType type = salaryField.getType();
                        if (type != null && SalaryFieldType.Code.Transient.equals(type.getCode())) {
                            //计算过渡项目,取合并计税表的内容作为最终内容
                            result.addData(field, part);
                            continue;
                        } else if (type != null && SalaryFieldType.Code.salary.equals(type.getCode())) {
                            //对于引用自员工薪资的项目我们是不做计算合并计算的。
                            AbstractSalaryFieldValue valueMeta = salaryField.getValueMeta();
                            FieldSource source = salaryField.getSource();
                            if (FieldSource.FUNCTION.equals(source) && valueMeta!=null && !FormulaFrom.USR.equals(valueMeta.getFormulaFrom())){
                                if (companyProfileSet.contains(salaryField.getCode())){
                                    result.addData(field, isNull(part) ? basedPart : (isNull(basedPart) ? part : basedPart));
                                }

                                continue;
                            }

                        }
                    }

                    result.addData(field, isNull(part) ? basedPart : (isNull(basedPart) ? part : combine(part, basedPart)));
                }
                mergeReportStaffSalary.setSummarizedData(result);
            }
            StaffSalarySnapshot summarizedData = mergeReportStaffSalary.getSummarizedData();
            if (termSalary!=null){
                summarizedData.addData(SyncedBasicStaffInfo.Field.staffName, termSalary.getStaffName());
                summarizedData.addData(SyncedBasicStaffInfo.Field.departmentName, termSalary.getDepartmentName());
                summarizedData.addData(SyncedBasicStaffInfo.Field.mobileNo, termSalary.getMobileNo());
            }else if (basedOnTermSalary!=null){
                summarizedData.addData(SyncedBasicStaffInfo.Field.staffName, basedOnTermSalary.getStaffName());
                summarizedData.addData(SyncedBasicStaffInfo.Field.departmentName, basedOnTermSalary.getDepartmentName());
                summarizedData.addData(SyncedBasicStaffInfo.Field.mobileNo, basedOnTermSalary.getMobileNo());
            }

            reports.add(mergeReportStaffSalary);
        }

        return reports;
    }

    private boolean isNull(DynamicCell cell){
        if (cell==null){
            return true;
        }
        if (cell.originValue()==null){
            return true;
        }
        return false;
    }

    private DynamicCell combine(DynamicCell part, DynamicCell basePart) {
        Object cellValue = part.originValue();
        Object basePartValue = basePart.originValue();
        if (cellValue == null) {
            return basePart;
        }
        if (basePart == null) {
            return part;
        }
        try {
            BigDecimal result = new BigDecimal(cellValue.toString()).add(new BigDecimal(basePartValue.toString()));
            return new DynamicCell(result.doubleValue());
        } catch (Exception e) {
            return basePartValue != null ? basePart : part;
        }
    }


    private ISalaryField getSalaryField(Map<String, SalaryField> salaryFieldMap, String field) {
        ISalaryField salaryField = salaryFieldMap.get(field);
        if (salaryField == null) {
            salaryField = salaryCalcContext.getSystemSalaryField(field);
        }
        return salaryField;
    }

    @AfterStep
    public void afterStep(){
        PayPeriodTerm payPeriodTerm = salaryCalcContext.payPeriodTerm;
        ValidationUtils.rejectIfEmpty(payPeriodTerm, "[薪资报表]", "无法获取账期薪资");
        Long salaryReportTermSalaryId = payPeriodTerm.getMergeSalaryReportTermSalaryId();
        MergeSalaryReportTermSalary mergeSalaryReportTermSalary = mergeSalaryReportTermSalaryRepository.findOne(salaryReportTermSalaryId);
        mergeSalaryReportTermSalary.setReportState(ReportState.FINISHED);
        mergeSalaryReportTermSalaryRepository.save(mergeSalaryReportTermSalary);
    }


    @BeforeProcess
    public void setSimpleStepContext(List<StaffPayPeriodTermSalary> staffPayPeriodTermSalaries, SimpleStepContext simpleStepContext) {
        this.simpleStepContext = simpleStepContext;
    }

}